/**
 * @(#)PursuitAlgorithmListPanel.java
 *
 *
 * @author Duyun Chen
 * @version 1.00 2008/10/5
 */
package Pursuit_Client;

import java.io.*;
import java.util.ArrayList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JList;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Dimension;
import javax.swing.BorderFactory;

public class PursuitAlgorithmListPanel extends JPanel 
{

	private static final long serialVersionUID = 1L;
	private DefaultListModel algorithmListModel = null;	//creates from AlgorithmList
	private static JList algorithmList = null;	//stores the list of algorithms
	private JScrollPane algorithmListScroller = null;
	private JButton addAlgorithmButton = new JButton("Add");
	private JButton removeAlgorithmButton = new JButton("Remove");
	private File classFilenamesFile = new File("Algorithms.dat");	//Files that contains the stored algorithm pathnames
//currently never gets set
	private Class<Object> currentActivePlayer = null;	//current working algorithm class

	private PursuitClient client;	//instance of the main GUI class (for listener)
	//two special manual algorithms controlled by user with arrow keys
	private PursuitManualChaser defaultChaser = new PursuitManualChaser();
	private PursuitManualChasee defaultChasee = new PursuitManualChasee();
	ArrayList<String> filepaths;
	
	//embedded class that stores each the information of each algorithm
	protected class Algorithm 
	{
		private File algorithmFile;
		private PursuitPlayer algorithmClass;
		
		/**
		 * Creates an algorithm object to be added to the panel
		 * @param f The file which holds the behavior code dictated for this algorithm.
		 * @param p A pursuit player which this algorithm is applied to.
		 */
		public Algorithm(File f, PursuitPlayer p) 
		{
			algorithmFile = f;
			algorithmClass = p;
		}
		public PursuitPlayer getPlayer() 
			{	return algorithmClass;	}	
		
		public File getAlgorithmFile() 
			{	return algorithmFile;	}
		
		public String toString() 
		{	return removeAddressExtension(algorithmClass.toString()) + " (" + algorithmClass.getRole() + ")";	}
	   
		private String removeAddressExtension(String in) 
		{
	    	int index = in.lastIndexOf("@");
	    	return index == -1? in:in.substring(0, index);
	    }
	}
	
	/**
	 * Creates an algorithm list panel with knowledge of a client
	 * @param pc The client that the list panel should be coupled with.
	 */
    public PursuitAlgorithmListPanel(PursuitClient pc) 
    {
    	super();
    	client = pc;	//this couples PursuitClient with PursuitAlgorithmListPanel
    	setLayout(new GridBagLayout());
    	setBorder(BorderFactory.createLoweredBevelBorder());
    	GridBagConstraints c = new GridBagConstraints();
    	
    	//set up algorithm list section
    	algorithmListModel = new DefaultListModel();
    	algorithmListModel.addElement(new Algorithm(null, defaultChaser));
    	algorithmListModel.addElement(new Algorithm(null, defaultChasee));
    	algorithmList = new JList(algorithmListModel);
    	PursuitClient.print("Loading existing algorithms...");
    	filepaths = loadAlgorithmsFromFile();
    	for (String algorithmPath:filepaths) 
    		addAlgorithm(new File(algorithmPath), false);

    	
    	algorithmListScroller = new JScrollPane(algorithmList, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
    	algorithmListScroller.setPreferredSize(new Dimension(200,350));
    	
    	//hook up buttons to pursuitclient for actions (clicks)
    	addAlgorithmButton.addActionListener(client);
    	removeAlgorithmButton.addActionListener(client);
    	
    	//setup layout
    	c.gridx = 0; c.gridy = 0; c.gridwidth = 2; c.fill = GridBagConstraints.BOTH;
    	add(algorithmListScroller, c);
    	c.gridx = 0; c.gridy = 1; c.gridwidth = 1;
    	add(addAlgorithmButton, c);
    	c.gridx = 1; c.gridy = 1;
    	add(removeAlgorithmButton, c);
    }
    
    /**
     * Gets the active player
     * @return An instance of Class<Object> 
     */
    public Class<Object> getActivePlayer() 
    	{	return currentActivePlayer;	}
    
  /**
   * This loads the predefined class names that are stored inside a .dat file
   * @return an ArrayList of type String with all the class names
   */
    private ArrayList<String> loadAlgorithmsFromFile() 
    {
    	ArrayList<String> algorithmNames = new ArrayList<String>();
    	BufferedReader reader = null;
		try 
		{
			if(classFilenamesFile.exists())
			{
		    	FileInputStream fstream = new FileInputStream(classFilenamesFile);		//init all the streams and readers
		 		InputStreamReader in = new InputStreamReader(fstream);
				reader = new BufferedReader(in);
				algorithmNames = processInputStream(reader);						//create the raw string
	    		reader.close();												//close file
			}
			else
			{
				FileWriter fw = new FileWriter(classFilenamesFile.getName());
				fw.write("");
				fw.close();
				PursuitClient.print("algorithms.dat created\n");
			
			}
    	}
    	catch (Exception e) 
    	{	PursuitClient.print("Error in loadAlgorithmsFromFile: " + e.getMessage());  	}
    	
    	return algorithmNames;
    }
    
    //appends the absolute path of an algorithm file to Algorithm.dat
    private void writeNewAlgorithmToFile(String FilePath) 
    {
    	try 
    	{
	        FileWriter fw = new FileWriter(classFilenamesFile.getName(),true);
	        fw.write(FilePath + '\n');//appends the string to the file
	        fw.close();
    	}
    	catch (IOException ioe) 
    	{	PursuitClient.print("Error in WriteNewAlgorithmToFile: " + ioe.getMessage());  	}
    }
    
    //reads every line from a BufferedReader and stores them in an ArrayList.
    private ArrayList<String> processInputStream(BufferedReader r) throws IOException
    {
    	ArrayList<String> buffer = new ArrayList<String>();
    	String line = null;
    	while ((line = r.readLine()) != null) 
    		buffer.add(line);
    	return buffer;
    }
    
    public static Algorithm getSelectedItem() 
    {  	return (Algorithm)algorithmList.getSelectedValue();   }
    
    /**
     * Adds an algorithm to the list panel
     * @param The file containing the algorithm
     * @param filewrite Boolean specifying whether or not to write to the algorithm.dat list file
     */
    public void addAlgorithm(File f, boolean filewrite) 
    {
    	//loads the selected class file in PursuitAlgorithmListPanel into an instance using ClassLoader
    	PursuitClassLoader loader = new PursuitClassLoader(f);
    	PursuitPlayer player = loader.getPlayer();
 
    	if (player == null) 
    	{
			PursuitClient.print("No algorithm added");
    		return;
    	}
    	
    	//determine the role of the algorithm. If not chaser, chasee, bug out.
    	if (!player.getRole().equals("chaser") && !player.getRole().equals("chasee")) 
    	{
    		PursuitClient.print("Error: Algorithm class provided an invalid role string. " + 
    							"Accepted role strings are \"chaser\" or \"chasee\" (case sensitive)");
    		return;
    	}

    	//Implement later when not so busy
    	//Check to see if the list already has an algorithm
    	//with the name about to be added
    	
    	/**
    	if (playerName)==duplicate)
    	{
    		PursuitClient.print(("Error: there is already a chaser or chasee with the same name."));
    	}
    	*/
    	
    	PursuitClient.print("Algorithm Added Successfully: " + player.getClass().getName() + " (" + player.getRole() + ")");
    	
    	//create and add algorithm object into GUI
    	Algorithm a = new Algorithm(f, player);
     	algorithmListModel.addElement(a);
    	algorithmList.setModel(algorithmListModel);
    	algorithmList.setSelectedIndex(0);	//select first value by default
    	//append the absolute path of the file in to Algorithm.dat
    	if (filewrite) 
    		writeNewAlgorithmToFile(a.getAlgorithmFile().getAbsolutePath());
    }
    
    public void removeAlgorithm() 
    {
    	int index = algorithmList.getSelectedIndex();
    	if (index == 0 || index == 1) 
    	{ //handle removing default exception
    		PursuitClient.print("Error: Cannot remove default algorithms");
    		return;
    	}
    	//Remove the algorithm entry from the algorithm path file
    	Algorithm a = (Algorithm)algorithmListModel.get(algorithmList.getSelectedIndex());
    	ArrayList<String> filePaths = loadAlgorithmsFromFile();
    	classFilenamesFile.delete();
    	
    	String path = null;
    	for (int ii = 0; ii < filePaths.size(); ii++) 
    	{
    		path = filePaths.get(ii);
    		if (path.equals(a.getAlgorithmFile().getAbsolutePath())) continue;
    		writeNewAlgorithmToFile(path);
    	}
    	
    	//Remove the algorithm entry from the GUI
    	algorithmListModel.removeElementAt(algorithmList.getSelectedIndex());
    	algorithmList.setModel(algorithmListModel);
    }
    
    public PursuitManualAlgorithm getDefaultChaser() 
    {	return defaultChaser; }
    public PursuitManualAlgorithm getDefaultChasee() 
    { 	return defaultChasee;   }
}